#include "stdafx.h"
#include "Config.h"
#include "Utility.h"
#include "SoundManager.h"
#include "MemTrack.h"

namespace bs
{
	const char* SOUND_SESSION = "sounds";

	SoundManager::SoundManager()
		: system(NULL), cfg(NULL), bgMusic(NULL), bgMusicName(""), enableSoundEffect(true), enableBgMusic(true), volume(0.6f), bgVolume(0.6f)
	{
		FMOD_RESULT result;

		result = FMOD::System_Create(&this->system);		// Create the main system object.

		if (result != FMOD_OK)
			return;

		unsigned int version;
		this->system->getVersion(&version);

		if(version < FMOD_VERSION)
			return ;

		this->system->init(100, FMOD_INIT_NORMAL, 0);	// Initialize FMOD.
	}

	SoundManager::~SoundManager()
	{
		SAFE_DELETE(this->cfg);

		for(std::map<std::string, SndUnit*>::iterator itr=this->sounds.begin();
			itr!=this->sounds.end(); ++itr)
		{
			SAFE_DELETE(itr->second)
		}

		this->system->release();
	}

	void SoundManager::AddSoundListener( const char* name, ISoundListener* listener )
	{
		this->listeners.insert(std::make_pair(name, listener));
	}

	void SoundManager::DetachListener( ISoundListener* listener )
	{
		for(std::multimap<std::string, ISoundListener*>::iterator itr=this->listeners.begin();
			itr!=this->listeners.end(); )
		{
			if(itr->second == listener)
			{
				itr = this->listeners.erase(itr);
			}
			else
			{
				++ itr;
			}
		}
	}

	void SoundManager::PlaySound(const char* name)
	{
		if(!this->enableSoundEffect)
			return;

		this->PlaySound(name, 0, this->volume);
	}

	void SoundManager::SetConfigFile( const char* file /*= "sound.ini"*/ )
	{
		if(!file || !::strlen(file))
			return;

		if(this->cfg)
			delete this->cfg;

		this->cfg = new Config;
		this->cfg->LoadFile(file);
	}

	void SoundManager::Update()
	{
		this->system->update();
	}

	void SoundManager::Pause()
	{
		for(std::map<std::string, SndUnit*>::iterator itr=this->sounds.begin();
			itr!=this->sounds.end(); ++itr)
		{
			itr->second->channel->setPaused(true);
		}
	}

	void SoundManager::Resume()
	{
		for(std::map<std::string, SndUnit*>::iterator itr=this->sounds.begin();
			itr!=this->sounds.end(); ++itr)
		{
			itr->second->channel->setPaused(false);
		}
	}

	void SoundManager::Stop()
	{
		for(std::map<std::string, SndUnit*>::iterator itr=this->sounds.begin();
			itr!=this->sounds.end(); ++itr)
		{
			itr->second->channel->stop();
		}
	}

	void SoundManager::PlayBgMusic( const char* name )
	{
		if(!this->enableBgMusic)
			return;

		this->bgMusic = this->PlaySound(name, -1, this->bgVolume);
	}

	void SoundManager::PauseBgMusic()
	{
		if(this->bgMusic)
		{
			this->bgMusic->channel->setPaused(true);
		}
	}

	void SoundManager::ResumeBgMusic()
	{
		if(this->bgMusic)
		{
			this->bgMusic->channel->setPaused(false);
		}
	}

	void SoundManager::StopBgMusic()
	{
		if(this->bgMusic)
		{
			this->bgMusic->channel->stop();
		}
	}

	void SoundManager::SetVolume( int vol )
	{
		this->volume = (float)vol / 100;

		for(std::map<std::string, SndUnit*>::iterator itr=this->sounds.begin();
			itr!=this->sounds.end(); ++itr)
		{
			itr->second->channel->setVolume(this->volume);
		}

		if(this->bgMusic)
		{
			this->bgMusic->channel->setVolume(this->bgVolume);
		}
	}

	void SoundManager::SetBgVolume( int vol )
	{
		this->bgVolume = (float)vol / 100;

		if(this->bgMusic)
		{
			this->bgMusic->channel->setVolume(this->bgVolume);
		}
	}

	FMOD_RESULT F_CALLBACK SoundManager::OnChannelEnd( FMOD_CHANNEL* channel, FMOD_CHANNEL_CALLBACKTYPE type, int command, unsigned int commanddata1, unsigned int commanddata2 )
	{
		if(type != FMOD_CHANNEL_CALLBACKTYPE_END)
			return FMOD_OK;

		SoundManager* manager = (SoundManager*)command;

		std::map<std::string, SndUnit*>::iterator itr=manager->sounds.begin();
		while(itr != manager->sounds.end())
		{
			if((int)itr->second->channel == (int)channel)
				break;

			++ itr;
		}

		if(itr == manager->sounds.end())
			return FMOD_OK;

		std::string name = itr->first;

		for(std::multimap<std::string, ISoundListener*>::iterator itr2=manager->listeners.lower_bound(name);
			itr2!=manager->listeners.upper_bound(name); ++itr2)
		{
			itr2->second->OnSoundEnd(name);
		}

		manager->listeners.erase(name);
		
		return FMOD_OK;
	}

	SoundManager::SndUnit* SoundManager::PlaySound( const char* name, int loop, float volume )
	{
		std::string file = name;
		if(this->cfg)
		{
			file = this->cfg->GetValue(SOUND_SESSION, name, "");
		}

		FMOD::Channel* channel = NULL;
		FMOD_RESULT result;	
		SndUnit* sndUnit = NULL;

		std::map<std::string, SndUnit*>::iterator itr = this->sounds.find(file); 
		if(itr != this->sounds.end())
		{
			result = system->playSound(FMOD_CHANNEL_FREE, itr->second->sound, true, &(itr->second->channel));

			if(result != FMOD_OK)
				return NULL;

			channel = itr->second->channel;
			sndUnit = itr->second;
		}
		else
		{
			FMOD::Sound* sound;

			result = this->system->createStream(file.c_str(), FMOD_HARDWARE|FMOD_LOOP_NORMAL|FMOD_2D, 0, &sound);

			if(result != FMOD_OK)
				return NULL;

			result = system->playSound(FMOD_CHANNEL_FREE, sound, true, &channel);

			if(result != FMOD_OK)
				return NULL;

			sndUnit = new SndUnit(sound, channel);

			this->sounds[file] = sndUnit;
		}

		channel->setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, &SoundManager::OnChannelEnd, (int)this);
		channel->setLoopCount(loop);
		channel->setVolume(this->volume);
		channel->setPaused(false);

		return sndUnit;
	}
}

